﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UdpPeer
{
    /// <summary>
    /// UDP节点 
    /// </summary>
    public class UdpPeerNode
    {
        /// <summary>
        /// UDP节占客户端对像
        /// </summary>
        private System.Net.Sockets.Socket client;

        /// <summary>
        /// 客户端对像
        /// </summary>
        public System.Net.Sockets.Socket Client
        {
            get { return client; }
        }

        /// <summary>
        /// 服务节点
        /// </summary>
        protected Socket ServerListen;

        /// <summary>
        /// 联接到远程点
        /// </summary>
        /// <param name="ep"></param>
        /// <returns></returns>
        public bool Connection(System.Net.IPEndPoint ep)
        {
            client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            SetSocketReuse(client);
            client.Connect(ep);

            return true;
        }

        /// <summary>
        /// 是否正在运行
        /// </summary>
        public bool IsRuning { get; set; }

        /// <summary>
        /// 开始
        /// </summary>
        public void Start()
        {
            Stop();
            //创建服务
            CreateServer();
            IsRuning = true;
        }
        /// <summary>
        /// 结束
        /// </summary>
        public void Stop()
        {
            IsRuning = false;
            try
            {
                ServerListen.Close();
                ServerListen.Dispose();
                ClientConnections.ToArray().ToList().ForEach((c) =>
                {
                    ConnClose(c.Key);
                });
            }
            catch (SocketException) { }
            catch (ObjectDisposedException) { }
            catch (NullReferenceException) { }
        }


        /// <summary>
        /// 向任意节点发数据
        /// </summary>
        /// <param name="data">向任意节点发数据</param>
        /// <param name="ep"></param>
        /// <returns></returns>
        public bool Send(byte[] data, EndPoint ep)
        {
            if (IsRuning)
            {
                ServerListen.SendTo(data, ep);

            }


            return true;

        }

        /// <summary>
        /// 创建Server
        /// </summary>
        /// <param name="ep">指定远程终端结点</param>
        public void CreateServer(EndPoint ep = null)
        {
            if (!IsRuning)
            {
                if (ep == null)
                {
                    //创建新的SocketServer
                    if (client != null && client.Connected)
                    {
                        ServerListen = new Socket(client.AddressFamily, client.SocketType, client.ProtocolType);
                        SetSocketReuse(ServerListen);
                        //用本地终节点邦定做服务端
                        ServerListen.Bind(client.LocalEndPoint);
                    }
                }
                else
                {
                    ServerListen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                    SetSocketReuse(ServerListen);
                    //用本地终节点邦定做服务端
                    ServerListen.Bind(ep);
                }
                ServerListen.DontFragment = true;
                //添加开始 accept
                PeerServerNodeThreadStudio.Instance.PlushAccept(StartServer);
            }
        }
        /// <summary>
        /// 调用接收数据
        /// </summary>
        protected void StartServer()
        {
            var args = RentReveiveSocketArgs();
            args.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
            byte[] bytes = new byte[65535 * 2];
            args.SetBuffer(bytes, 0, bytes.Length);
            StartReceiveFromAsync(args);
            DoStatusHandler("开始接收UDP数据...邦定节点" + ServerListen.LocalEndPoint);
        }

        /// <summary>
        /// 设置端口,地址重用
        /// </summary>
        /// <param name="socket">socket</param>
        public static void SetSocketReuse(Socket socket)
        {
            //可以帮定同一端口
            //表示IP端口重用
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

        }

        #region 客户端联接池
        /// <summary>
        /// 接收SAEA池集合
        /// </summary>
        private ConcurrentStack<SocketAsyncEventArgs> ReceiveSocketArgsPool = new ConcurrentStack<SocketAsyncEventArgs>();

        /// <summary>
        /// 初始化接收socketargs对象池
        /// </summary>
        protected void InitReceiveSocketArgsPool()
        {
            for (int ndx = 0; ndx < 3000; ndx++)
            {
                ReceiveSocketArgsPool.Push(CreateReceiveSocketArgs());
            }
        }

        /// <summary>
        /// 创建一个接收SAEA对象,设置最大接收字节数
        /// </summary>
        /// <returns></returns>
        protected virtual SocketAsyncEventArgs CreateReceiveSocketArgs()
        {
            SocketAsyncEventArgs e = new SocketAsyncEventArgs();
            e.Completed += IO_Comleted;
            return e;
        }




        /// <summary>
        /// 租赁一个接收SAEA对象
        /// </summary>
        /// <returns></returns>
        protected virtual SocketAsyncEventArgs RentReveiveSocketArgs()
        {
            SocketAsyncEventArgs e;

            return ReceiveSocketArgsPool.TryPop(out e) ? e : CreateReceiveSocketArgs();
        }

        /// <summary>
        /// 归还一个接收SAEA参数
        /// </summary>
        /// <param name="e">还池</param>
        protected virtual void GivebackReceiveSocketArgs(SocketAsyncEventArgs e)
        {
            if (e != null)
            {
                e.UserToken = null;
                ReceiveSocketArgsPool.Push(e);
            }
        }

        #endregion


        #region 接收数据

        /// <summary>
        /// 客户端联接
        /// </summary>
        private System.Collections.Concurrent.ConcurrentDictionary<string, EndPoint> cientConnections = new ConcurrentDictionary<string, EndPoint>();
        /// <summary>
        /// 客户端联接
        /// </summary>
        public System.Collections.Concurrent.ConcurrentDictionary<string, EndPoint> ClientConnections
        {
            get { return cientConnections; }
        }

        /// <summary>
        /// 接收数据Handler,数据来源节点,用于处理的异步对像
        /// </summary>
        public Action<byte[], System.Net.EndPoint> ReceiveHandler { get; set; }

        /// <summary>
        ///状态
        /// </summary>
        public Action<string> StatusHandler { get; set; }

        /// <summary>
        ///  应用状态数据
        /// </summary>
        /// <param name="txt">状态信息</param>
        protected void DoStatusHandler(string txt)
        {
            if (StatusHandler != null)
            {
                StatusHandler(txt);
            }
        }
        /// <summary>
        ///  应用状态数据
        /// </summary>
        /// <param name="txt">状态信息</param>
        protected void DoStatusHandler(Exception txt) { DoStatusHandler(txt.ToString()); }
        /// <summary>
        /// 开始接收数据
        /// </summary>
        /// <param name="e"></param>
        protected void StartReceiveFromAsync(SocketAsyncEventArgs e)
        {
            try
            {
                if (!ServerListen.ReceiveFromAsync(e))
                {
                    IO_Comleted(this, e);
                }
            }
            catch (Exception ex) { DoStatusHandler(ex); }


        }
        /// <summary>
        /// 接收后的完成功能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void IO_Comleted(object sender, SocketAsyncEventArgs e)
        {
            try
            {

                //发送数据OK
                if (e.SocketError == SocketError.Success)
                {
                    if (e.BytesTransferred > 0)
                    {
                        string key = e.RemoteEndPoint.ToString();
                        if (!ClientConnections.ContainsKey(key))
                        {
                            ClientConnections.TryAdd(key, e.RemoteEndPoint);
                        }
                        //转入外部委托处理
                        if (ReceiveHandler != null)
                        {
                            byte[] array = new byte[e.BytesTransferred];
                            Buffer.BlockCopy(e.Buffer, 0, array, 0, array.Length);
                            ReceiveHandler(array, e.RemoteEndPoint);
                        }
                    }
                    else
                    {
                        throw new SocketException();
                    }
                }

            }
            finally
            {
                //拉收数据
                StartReceiveFromAsync(e);
            }
        }
        protected void ConnClose(string key)
        {

            //移除对像
            EndPoint xt;
            ClientConnections.TryRemove(key, out xt);
            DoStatusHandler("移除节点:" + key);
        }

        #endregion

        /// <summary>
        /// 服务节点线程处理,工具
        /// </summary>
        internal class PeerServerNodeThreadStudio
        {

            private PeerServerNodeThreadStudio() { Init(); }

            private static readonly PeerServerNodeThreadStudio instance = new PeerServerNodeThreadStudio();
            /// <summary>
            /// 访问单例
            /// </summary>
            public static PeerServerNodeThreadStudio Instance
            {
                get { return PeerServerNodeThreadStudio.instance; }
            }




            /// <summary>
            /// 添加一个接入
            /// </summary>
            public void PlushAccept(Action act)
            {
                actqueue.Enqueue(act);
            }

            Queue<Action> actqueue = new Queue<Action>();

            /// <summary>
            /// 开始初始化
            /// </summary>
            protected void Init()
            {


                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        Action act = null;
                        if (actqueue.Count > 0)
                        {
                            act = actqueue.Dequeue();
                        }
                        if (act != null)
                        {
                            //所有的都加入到一个线程中进行处理.
                            act();
                        }
                        else
                        {
                            System.Threading.Thread.Sleep(10);
                        }
                    }
                });
            }


        }



    }
}
